Program Listing
     1  // ex_counter.nxc
     2  // Simulation of a digital counter odometer-like display. The
     3  // counter digits are displayed in seven-segment form such as
     4  // used in many electronic instrument displays.
     5  
     6  // Right button increments count.
     7  // Left button decrements count.
     8  // Center button zeros count.
     9  // Gray button halts and exits program.
    10  
    11  // Buttons must be bumped (pressed and released) for operations to
    12  // execute.
    13  
    14  // DIGITS determines how many digits the counter maintains and
    15  // displays. It should no bigger than 5. If not set at compile
    16  // time, it will default to 3.
    17  #ifndef DIGITS
    18  #define DIGITS 3
    19  #endif
    20  
    21  // Counter globals
    22  mutex counter_mutex;
    23  bool counter_changed;
    24  int counter[DIGITS];
    25  
    26  // Section defining 7-segment display functions
    27  
    28  // Dimensions of the segments of a 7-segment digit
    29  #define HRZ_W 8
    30  #define HRZ_H 2
    31  #define VRT_W 2
    32  #define VRT_H 8
    33  
    34  // Dimensions of the spaces occupied by the segments.
    35  #define HRZ_CELL_W (HRZ_W + 1)
    36  #define HRZ_CELL_H (HRZ_H + 1)
    37  #define VRT_CELL_W (VRT_W + 1)
    38  #define VRT_CELL_H (VRT_H + 1)
    39  
    40  // Constants used to position digits on the display.
    41  #define DIGIT_WIDTH (HRZ_CELL_W + 2 * VRT_CELL_W + 2)
    42  #define COUNTER_WIDTH (DIGIT_WIDTH * DIGITS)
    43  #define X_OFFSET ((100 - COUNTER_WIDTH) / 2)
    44  #define DIGIT_HEIGHT (3 * HRZ_CELL_H + 2 * VRT_CELL_H)
    45  #define Y_OFFSET ((64 - DIGIT_HEIGHT) / 2)
    46  
    47  // Draw a horizontal segment at screen location (x, y), which is at  
    48  // the lower-left corner of the segment.
    49  inline void hrz_segment_out(int x, int y)
    50  {
    51     RectOut(x, y, HRZ_W, HRZ_H, DRAW_OPT_FILL_SHAPE);
    52  }
    53  
    54  // Draw a vertical segment at screen location (x, y), which is at  
    55  // the lower-left corner of the segment.
    56  inline void vrt_segment_out(int x, int y)
    57  {
    58     RectOut(x, y, VRT_W, VRT_H, DRAW_OPT_FILL_SHAPE);
    59  }
    60  
    61  /*
    62      aaa
    63     e   b
    64     e   b
    65     e   b
    66      ggg       the segments of a 7-segment display
    67     f   c
    68     f   c
    69     f   c
    70      ddd
    71      
    72      The reference point (ref_x, ref_y) for positioning a 7-segment
    73      digit on the NXT screen is at its lower right corner.
    74  */
    75  
    76  // Draw segment aaa at its proper offset from the reference point
    77  // (ref_x, ref_y).
    78  inline void seg_a_out(int ref_x, int ref_y)
    79  {
    80     const int dx = VRT_CELL_W;
    81     const int dy = 2 * HRZ_CELL_H + 2 * VRT_CELL_H;
    82     hrz_segment_out(ref_x + dx, ref_y + dy);
    83  }
    84  
    85  // Draw segment bbb at its proper offset from the reference point
    86  // (ref_x, ref_y).
    87  inline void seg_b_out(int ref_x, int ref_y)
    88  {
    89     const int dx = VRT_CELL_W + HRZ_CELL_W;
    90     const int dy = 2 * HRZ_CELL_H + VRT_CELL_H;
    91     vrt_segment_out(ref_x + dx, ref_y + dy);
    92  }
    93  
    94  // Draw segment ccc at its proper offset from the reference point
    95  // (ref_x, ref_y).
    96  inline void seg_c_out(int ref_x, int ref_y)
    97  {
    98     const int dx = VRT_CELL_W + HRZ_CELL_W;
    99     const int dy = HRZ_CELL_H;
   100     vrt_segment_out(ref_x + dx, ref_y + dy);
   101  }
   102  
   103  // Draw segment ddd at its proper offset from the reference point
   104  // (ref_x, ref_y).
   105  inline void seg_d_out(int ref_x, int ref_y)
   106  {
   107     const int dx = VRT_CELL_W;
   108     const int dy = 0;
   109     hrz_segment_out(ref_x + dx, ref_y + dy);
   110  }
   111  
   112  // Draw segment eee at its proper offset from the reference point
   113  // (ref_x, ref_y).
   114  inline void seg_e_out(int ref_x, int ref_y)
   115  {
   116     const int dx = 0;
   117     const int dy = 2 * HRZ_CELL_H + VRT_CELL_H;
   118     vrt_segment_out(ref_x + dx, ref_y + dy);
   119  }
   120  
   121  // Draw segment fff at its proper offset from the reference point
   122  // (ref_x, ref_y).
   123  inline void seg_f_out(int ref_x, int ref_y)
   124  {
   125     const int dx = 0;
   126     const int dy = HRZ_CELL_H;
   127     vrt_segment_out(ref_x + dx, ref_y + dy);
   128  }
   129  
   130  // Draw segment ggg at its proper offset from the reference point
   131  // (ref_x, ref_y).
   132  inline void seg_g_out(int ref_x, int ref_y)
   133  {
   134     const int dx = VRT_CELL_W;
   135     const int dy = HRZ_CELL_H + VRT_CELL_H;
   136     hrz_segment_out(ref_x + dx, ref_y + dy);
   137  }
   138  
   139  // 7-segment numbers on the display are built up from various
   140  // combinations of horizontal and vertical segments.
   141  
   142  // Draw zero at reference point (x, Y_OFFSET).
   143  void digit_0(int x)
   144  {
   145     seg_a_out(x, Y_OFFSET);
   146     seg_b_out(x, Y_OFFSET);
   147     seg_c_out(x, Y_OFFSET);
   148     seg_d_out(x, Y_OFFSET);
   149     seg_e_out(x, Y_OFFSET);
   150     seg_f_out(x, Y_OFFSET);
   151  }
   152  
   153  // Draw one at reference point (x, Y_OFFSET).
   154  void digit_1(int x)
   155  {
   156     seg_b_out(x, Y_OFFSET);
   157     seg_c_out(x, Y_OFFSET);
   158  }
   159  
   160  // Draw twp at reference point (x, Y_OFFSET).
   161  void digit_2(int x)
   162  {
   163     seg_a_out(x, Y_OFFSET);
   164     seg_b_out(x, Y_OFFSET);
   165     seg_d_out(x, Y_OFFSET);
   166     seg_f_out(x, Y_OFFSET);
   167     seg_g_out(x, Y_OFFSET);
   168  }
   169  
   170  // Draw three at reference point (x, Y_OFFSET).
   171  void digit_3(int x)
   172  {
   173     seg_a_out(x, Y_OFFSET);
   174     seg_b_out(x, Y_OFFSET);
   175     seg_c_out(x, Y_OFFSET);
   176     seg_d_out(x, Y_OFFSET);
   177     seg_g_out(x, Y_OFFSET);
   178  }
   179  
   180  // Draw four at reference point (x, Y_OFFSET).
   181  void digit_4(int x)
   182  {
   183     seg_b_out(x, Y_OFFSET);
   184     seg_c_out(x, Y_OFFSET);
   185     seg_e_out(x, Y_OFFSET);
   186     seg_g_out(x, Y_OFFSET);
   187  }
   188  
   189  // Draw five at reference point (x, Y_OFFSET).
   190  void digit_5(int x)
   191  {
   192     seg_a_out(x, Y_OFFSET);
   193     seg_c_out(x, Y_OFFSET);
   194     seg_d_out(x, Y_OFFSET);
   195     seg_e_out(x, Y_OFFSET);
   196     seg_g_out(x, Y_OFFSET);
   197  }
   198  
   199  // Draw six at reference point (x, Y_OFFSET).
   200  void digit_6(int x)
   201  {
   202     seg_a_out(x, Y_OFFSET);
   203     seg_c_out(x, Y_OFFSET);
   204     seg_d_out(x, Y_OFFSET);
   205     seg_e_out(x, Y_OFFSET);
   206     seg_f_out(x, Y_OFFSET);
   207     seg_g_out(x, Y_OFFSET);
   208  }
   209  
   210  // Draw seven at reference point (x, Y_OFFSET).
   211  void digit_7(int x)
   212  {
   213     seg_a_out(x, Y_OFFSET);
   214     seg_b_out(x, Y_OFFSET);
   215     seg_c_out(x, Y_OFFSET);
   216     seg_e_out(x, Y_OFFSET);
   217  }
   218  
   219  // Draw eight at reference point (x, Y_OFFSET).
   220  void digit_8(int x)
   221  {
   222     seg_a_out(x, Y_OFFSET);
   223     seg_b_out(x, Y_OFFSET);
   224     seg_c_out(x, Y_OFFSET);
   225     seg_d_out(x, Y_OFFSET);
   226     seg_e_out(x, Y_OFFSET);
   227     seg_f_out(x, Y_OFFSET);
   228     seg_g_out(x, Y_OFFSET);
   229  }
   230  
   231  // Draw nine at reference point (x, Y_OFFSET).
   232  void digit_9(int x)
   233  {
   234     seg_a_out(x, Y_OFFSET);
   235     seg_b_out(x, Y_OFFSET);
   236     seg_c_out(x, Y_OFFSET);
   237     seg_d_out(x, Y_OFFSET);
   238     seg_e_out(x, Y_OFFSET);
   239     seg_g_out(x, Y_OFFSET);
   240  }
   241  
   242  // Given the digit place of digit, return its horizontal offset on
   243  // the NXT screen. The place of a digit is its index in the counter
   244  // array.
   245  int offset(int place)
   246  {
   247     return X_OFFSET + (DIGITS - place - 1) * DIGIT_WIDTH;
   248  }
   249  
   250  // Section defining counter control tasks
   251  
   252  // Given a button's ID number, return true if that button has been
   253  // bumped. Otherwise return false.
   254  bool button_bumped(const byte btn)
   255  {
   256     bool result = false;
   257     if (ButtonPressed(btn, false))
   258     {
   259        while (ButtonPressed(btn, true));
   260        PlayFile("! Click.rso");
   261        result = true;
   262     }
   263     return result;
   264  }
   265  
   266  // When the right button is bumped, increment the counter. The
   267  // for-loop performs carry propagation.
   268  task increment()
   269  {
   270     byte carry;
   271     while(true)
   272     {
   273        Acquire(counter_mutex);
   274        if ((! counter_changed) && button_bumped(BTNRIGHT))
   275        {
   276           carry = 1;
   277           for (int i = 0; (carry == 1) && (i < DIGITS); ++i)
   278           {
   279              int d = counter[i] + carry;
   280              if (d > 9)
   281              {
   282                 d = 0;
   283                 carry = 1;
   284              }
   285              else carry = 0;
   286              counter[i] = d;
   287           }
   288           counter_changed = true;
   289        }
   290        Release(counter_mutex);
   291        Yield();
   292     }
   293  }
   294  
   295  // When the left button is bumped, decrement the counter. The
   296  // for-loop performs borrow propagation.
   297  task decrement()
   298  {
   299     byte borrow;
   300     while(true)
   301     {
   302        Acquire(counter_mutex);
   303        if ((! counter_changed) && button_bumped(BTNLEFT))
   304        {
   305           borrow = 1;
   306           for (int i = 0; (borrow == 1) && (i < DIGITS); ++i)
   307           {
   308              int d = counter[i] - borrow;
   309              if (d < 0)
   310              {
   311                 d = 9;
   312                 borrow = 1;
   313              }
   314              else borrow = 0;
   315              counter[i] = d;
   316           }
   317           counter_changed = true;
   318        }
   319        Release(counter_mutex);
   320        Yield();
   321     }
   322  }
   323  
   324  // When the center button is bumped, zero the counter.
   325  task zero()
   326  {
   327     while(true)
   328     {
   329        Acquire(counter_mutex);
   330        if ((! counter_changed) && button_bumped(BTNCENTER))
   331        {
   332           ArrayInit(counter, 0, DIGITS);
   333           counter_changed = true;
   334        }
   335        Release(counter_mutex);
   336        Yield();
   337     }
   338  }
   339  
   340  // When the count has changed, update the NXT screen with the new
   341  // count.
   342  task show_count()
   343  {
   344     while(true)
   345     {
   346        Acquire(counter_mutex);
   347        if (counter_changed)
   348        {
   349           ClearScreen();
   350           for (int i = 0; i < DIGITS; ++i)
   351           {
   352              switch (counter[i])
   353              {
   354              case 0:
   355                 digit_0(offset(i));
   356                 break;
   357              case 1:
   358                 digit_1(offset(i));
   359                 break;
   360              case 2:
   361                 digit_2(offset(i));
   362                 break;
   363              case 3:
   364                 digit_3(offset(i));
   365                 break;
   366              case 4:
   367                 digit_4(offset(i));
   368                 break;
   369              case 5:
   370                 digit_5(offset(i));
   371                 break;
   372              case 6:
   373                 digit_6(offset(i));
   374                 break;
   375              case 7:
   376                 digit_7(offset(i));
   377                 break;
   378              case 8:
   379                 digit_8(offset(i));
   380                 break;
   381              case 9:
   382                 digit_9(offset(i));
   383                 break;
   384              }
   385           }
   386           counter_changed = false;
   387        }
   388        Release(counter_mutex);
   389        Yield();
   390     }
   391  }
   392  
   393  // Section defining start-up code.
   394  
   395  task main()
   396  {
   397     Precedes(show_count, zero, increment, decrement);
   398     counter = 0;
   399     counter_changed = true;
   400  }